const stringifyForLog = (value, max = 800) => {
  if (value === undefined || value === null) {
    return value;
  }
  if (typeof value === 'string') {
    return value.length > max ? `${value.slice(0, max)}...` : value;
  }
  try {
    const str = JSON.stringify(value);
    return str.length > max ? `${str.slice(0, max)}...` : str;
  } catch (error) {
    return String(value);
  }
};

import { sanitizeChromeInteractiveElementsResult } from '../lib/interactive-elements.js';
import parseJDProductsScript from '../crawl/parse-jd-products.js';

const createEmptyResult = () => ({ domSnapshots: [], productItems: [] });

const shouldUseInjectScript = (selector) => {
  if (!selector || typeof selector !== 'string') {
    return false;
  }
  return /_goods_title_container/.test(selector);
};

const buildInjectScript = () => parseJDProductsScript;

const INJECT_EVENT_NAME = 'parse-jd-products';

const tryParseJson = (value) => {
  if (typeof value !== 'string' || !value.trim()) {
    return null;
  }
  try {
    return JSON.parse(value);
  } catch (_) {
    return null;
  }
};

const normalizeResult = (value) => {
  if (!value) {
    return null;
  }

  if (Array.isArray(value)) {
    return { ...createEmptyResult(), productItems: value };
  }

  if (typeof value === 'string') {
    const parsed = tryParseJson(value);
    if (parsed) {
      return normalizeResult(parsed);
    }
    return null;
  }

  if (typeof value !== 'object') {
    return null;
  }

  if (Array.isArray(value.productItems) || Array.isArray(value.domSnapshots)) {
    return {
      productItems: Array.isArray(value.productItems) ? value.productItems : [],
      domSnapshots: Array.isArray(value.domSnapshots) ? value.domSnapshots : []
    };
  }

  return null;
};

const extractFromPayload = (payload, visited = new Set()) => {
  if (payload == null) {
    return createEmptyResult();
  }

  if (typeof payload === 'string' || Array.isArray(payload)) {
    return normalizeResult(payload) || createEmptyResult();
  }

  if (typeof payload !== 'object') {
    return createEmptyResult();
  }

  if (visited.has(payload)) {
    return createEmptyResult();
  }
  visited.add(payload);

  const direct = normalizeResult(payload);
  if (direct) {
    return direct;
  }

  const candidates = [];

  if (Array.isArray(payload.content)) {
    candidates.push(...payload.content);
  }

  if (Array.isArray(payload.data?.content)) {
    candidates.push(...payload.data.content);
  }

  if (payload.primary) {
    candidates.push(payload.primary);
  }

  if (payload.inject) {
    candidates.push(payload.inject);
  }

  if (payload.value !== undefined) {
    candidates.push(payload.value);
  }

  if (payload.text !== undefined) {
    candidates.push(payload.text);
  }

  if (payload.result !== undefined) {
    candidates.push(payload.result);
  }

  if (Array.isArray(payload.results)) {
    candidates.push(...payload.results);
  }

  if (Array.isArray(payload.messages)) {
    candidates.push(...payload.messages);
  }

  if (Array.isArray(payload.logs)) {
    candidates.push(...payload.logs);
  }

  if (payload.message) {
    candidates.push(payload.message);
  }

  for (const candidate of candidates) {
    if (candidate == null) {
      continue;
    }

    if (typeof candidate === 'string' || Array.isArray(candidate)) {
      const directCandidate = normalizeResult(candidate);
      if (directCandidate) {
        return directCandidate;
      }
      if (typeof candidate === 'string') {
        const parsed = tryParseJson(candidate);
        if (parsed) {
          const nested = extractFromPayload(parsed, visited);
          if (nested) {
            return nested;
          }
        }
      }
      continue;
    }

    if (typeof candidate === 'object') {
      const nestedDirect = normalizeResult(candidate);
      if (nestedDirect) {
        return nestedDirect;
      }

      if (candidate.value !== undefined) {
        const nested = extractFromPayload(candidate.value, visited);
        if (nested) {
          return nested;
        }
      }

      if (candidate.text !== undefined) {
        const nested = extractFromPayload(candidate.text, visited);
        if (nested) {
          return nested;
        }
      }

      if (candidate.result !== undefined) {
        const nested = extractFromPayload(candidate.result, visited);
        if (nested) {
          return nested;
        }
      }

      if (Array.isArray(candidate.content)) {
        const nested = extractFromPayload({ content: candidate.content }, visited);
        if (nested) {
          return nested;
        }
      }

      if (Array.isArray(candidate.data?.content)) {
        const nested = extractFromPayload({ content: candidate.data.content }, visited);
        if (nested) {
          return nested;
        }
      }
    }
  }

  return createEmptyResult();
};

const parseInjectResult = (payload) => extractFromPayload(payload);

const buildSendCommandArgs = (jsScript) => ({
  command: 'Runtime.evaluate',
  eventName: INJECT_EVENT_NAME,
  timeoutMs: 60000,
  params: {
    expression: jsScript,
    returnByValue: true,
    awaitPromise: true
  }
});

export const chromeGetInteractiveElementsTool = {
  name: 'chrome_get_interactive_elements',
  async doCall({ mcpClient, args, toolName }) {
    console.log('[Tools][chrome_get_interactive_elements] doCall args', { toolName, args: stringifyForLog(args) });
    const primaryResult = await mcpClient.callTool(toolName, args);
    console.log('[Tools][chrome_get_interactive_elements] primary result', stringifyForLog(primaryResult));

    if (!shouldUseInjectScript(args?.selector)) {
      console.log('[Tools][chrome_get_interactive_elements] skip inject path');
      return primaryResult;
    }

    try {
      const jsScript = buildInjectScript();
      console.log('[Tools][chrome_get_interactive_elements] invoking inject script');
      const injectResult = await mcpClient.callTool('chrome_send_command_to_inject_script', buildSendCommandArgs(jsScript));
      const combined = {
        primary: primaryResult,
        inject: injectResult
      };
      console.log('[Tools][chrome_get_interactive_elements] inject result', stringifyForLog(combined));
      return combined;
    } catch (error) {
      console.warn('[Tools] chrome_send_command_to_inject_script fallback 执行失败:', error);
      return primaryResult;
    }
  },
  async getToolRawResult(callResult) {
    console.log('[Tools][chrome_get_interactive_elements] getToolRawResult input', stringifyForLog(callResult));
    return callResult;
  },
  async getToolTextResult(rawResult, context) {
    console.log('[Tools][chrome_get_interactive_elements] getToolTextResult input', stringifyForLog(rawResult));
    if (rawResult?.primary || rawResult?.inject) {
      const sanitized = sanitizeChromeInteractiveElementsResult(rawResult.primary ?? rawResult);
      if (rawResult.inject) {
        const { domSnapshots, productItems } = parseInjectResult(rawResult.inject);
        if (domSnapshots.length) {
          sanitized.domSnapshots = domSnapshots;
        }
        if (productItems.length) {
          sanitized.productItems = productItems;
        }
      }
      console.log('[Tools][chrome_get_interactive_elements] getToolTextResult output', stringifyForLog(sanitized));
      return sanitized;
    }

    const sanitized = sanitizeChromeInteractiveElementsResult(rawResult);
    if (shouldUseInjectScript(context?.args?.selector)) {
      try {
        const jsScript = buildInjectScript();
        console.log('[Tools][chrome_get_interactive_elements] delayed inject path triggered');
        const injectResult = await context.mcpClient.callTool('chrome_send_command_to_inject_script', buildSendCommandArgs(jsScript));
        const { domSnapshots, productItems } = parseInjectResult(injectResult);
        if (domSnapshots.length) {
          sanitized.domSnapshots = domSnapshots;
        }
        if (productItems.length) {
          sanitized.productItems = productItems;
        }
      } catch (error) {
        console.warn('[Tools] chrome_send_command_to_inject_script fallback 执行失败:', error);
      }
    }
    console.log('[Tools][chrome_get_interactive_elements] getToolTextResult output', stringifyForLog(sanitized));
    return sanitized;
  }
};
